import html
import json
import asyncio
import re
import websockets
from typing import Dict, Any
import os
import requests

from .notification import get_notifier
from .utils.logger import get_logger
from .config import Config

logger = get_logger(__name__)

class WebSocketClient:
    def __init__(self, config: Config):
        self.config = config
        self.notifier = get_notifier()
        self.ws = None
        self.should_run = True
        self._loop = None
        self._status_callback = None
        self._connected = False
        self._connection_lock = asyncio.Lock()
        self._token = self._load_token()  # 缓存 Token

    @property
    def is_connected(self) -> bool:
        """获取当前连接状态"""
        return self._connected and self.ws is not None and not getattr(self.ws, 'closed', True)

    def set_loop(self, loop):
        """设置事件循环"""
        self._loop = loop

    def set_status_callback(self, callback):
        """设置状态更新回调"""
        self._status_callback = callback

    def _update_status(self, connected: bool):
        """更新连接状态"""
        try:
            if self._connected != connected:
                self._connected = connected
                if self._status_callback:
                    if self._loop and self._loop.is_running():
                        self._loop.call_soon_threadsafe(self._status_callback, connected)
                    else:
                        self._status_callback(connected)
        except Exception as e:
            logger.error(f"Error updating status: {e}")

    def _get_ws_url(self):
        """获取带认证的 WebSocket URL"""
        try:
            if os.path.exists(self.config.ACCOUNT_PATH):
                with open(self.config.ACCOUNT_PATH, 'r', encoding='utf-8') as f:
                    settings = json.load(f)
                    token = settings.get('token', '')
                    if token:
                        # 更新token，避免用户更换了token，缓存的没有更新
                        self._token = token
                        from urllib.parse import urlparse, urlencode
                        url_parts = urlparse(self.config.WS_URL)
                        query_params = {
                            'action': 'web',
                            'token': token,
                            'language': 'zh'
                        }
                        new_query = urlencode(query_params)
                        return f"{url_parts.scheme}://{url_parts.netloc}{url_parts.path}?{new_query}"
            logger.warning("No valid token found")
            return self.config.WS_URL
        except Exception as e:
            logger.error(f"Error getting WebSocket URL: {e}")
            return self.config.WS_URL

    async def connect(self):
        """连接 WebSocket"""
        async with self._connection_lock:
            attempt = 0  # 添加重连尝试计数器
            max_attempts = 10  # 最大重连次数
            while self.should_run and attempt < max_attempts:
                try:
                    ws_url = self._get_ws_url()
                    logger.info(f"准备连接 {ws_url} (尝试 {attempt + 1}/{max_attempts})")
                    async with websockets.connect(ws_url) as websocket:
                        self.ws = websocket
                        logger.info(f"已连接到 {ws_url}")
                        self._update_status(True)
                        await self._handle_messages()
                except websockets.ConnectionClosed:
                    logger.info("WebSocket connection closed normally")
                    self._update_status(False)
                except Exception as e:
                    logger.error(f"连接错误: {e}")
                    self._update_status(False)
                    attempt += 1  # 增加尝试计数
                    if attempt >= max_attempts:
                        logger.error("达到最大重连次数，停止重连")
                        # 通知用户连接失败
                        self.notifier.notify(
                            title="连接失败",
                            message="多次尝试连接失败，请检查配置后重试。"
                        )
                        break
                    if self.should_run:
                        await asyncio.sleep(self.config.RECONNECT_DELAY)
                finally:
                    self.ws = None
                    self._update_status(False)

    async def _handle_messages(self):
        try:
            async for message in self.ws:
                try:
                    data = json.loads(message)
                    logger.info("接收消息：%s", data)
                    await self._process_message(data)
                except json.JSONDecodeError:
                    logger.error(f"Invalid JSON message: {message}")
        except websockets.ConnectionClosed:
            logger.info("WebSocket connection closed")
            self._update_status(False)
    
    """
        {
            "type": "dialog",
            "mode": "add",
            "silence": 0,
            "data": {
                "id": 62,
                "dialog_id": 6,
                "dialog_type": "user",
                "userid": 4, //发送消息的用户id
                "type": "text",
                "mtype": "text",
                "msg": {
                    "text": "<p>你好</p>" //聊天内容
                },
                "emoji": [],
                "read": 0,
                "send": 1,
                "tag": 0,
                "todo": 0,
                "link": 0,
                "modify": 0,
                "reply_num": 0,
                "reply_id": 0,
                "forward_id": 0,
                "forward_num": 0,
                "created_at": "2025-03-12 17:44:48",
                "deleted_at": null,
                "is_remind": null,
                "percentage": 0,
                "mention": 0,
                "dot": 0,
                "user_at": "2025-03-12 17:44:48.217",
                "user_ms": 1741772688217
            }
        }
    """
    async def _process_message(self, data: Dict[str, Any]):
        """处理接收到的消息"""
        try:
            message_type = data.get('type')

            if message_type != "dialog":
                logger.info("非对话消息，忽略!")
                return

            title = data.get('title', '消息通知')
            content = data.get('data', {})["msg"]
            userid = data.get('data', {})["userid"]
            priority = data.get('priority', 'normal')
            nickname = ""
            
            logger.info(f"处理消息: 类型={message_type}, 优先级={priority}, 内容={content}")

            userInfo = self.get_user_info(userid)
            logger.info(f"==>用户信息：{userInfo}")
            if userInfo["data"] and len(userInfo["data"]) > 0:
                nickname = f"{userInfo["data"][0]["nickname"]}："            

            # 发送通知
            self.notifier.notify(
                title=title,
                message= nickname + self.__clean_html_tags(content["text"]),
                timeout=self.config.NOTIFICATION_TIMEOUT
            )
            logger.info("通知发送成功")
            
        except Exception as e:
            logger.error(f"处理消息失败: {e}")

    def __clean_html_tags(self, content_html: str) -> str:
        if not content_html:
            return ""
        # 使用 html.parser 将 HTML 转换为纯文本
        content = html.unescape(content_html)
        content = re.sub('<[^<]+?>', '', content)  # 移除 HTML 标签
        content = re.sub('[\r\n\t]+', ' ', content)  # 移除回车、换行、制表符
        return content.strip()
    
    def stop(self):
        """停止 WebSocket 客户端"""
        self.should_run = False
        if self.ws and self._loop:
            try:
                # 在原有的事件循环中关闭连接
                future = asyncio.run_coroutine_threadsafe(
                    self.ws.close(),
                    self._loop
                )
                # 等待关闭完成，设置超时
                future.result(timeout=5)
                self._update_status(False)  # 更新为未连接状态
            except Exception as e:
                logger.error(f"Error closing WebSocket: {e}") 

    def _load_token(self) -> str:
        """从配置文件中加载 token"""
        try:
            if os.path.exists(self.config.ACCOUNT_PATH):
                with open(self.config.ACCOUNT_PATH, 'r', encoding='utf-8') as f:
                    settings = json.load(f)
                    return settings.get('token', '')
        except Exception as e:
            logger.error(f"Error loading token: {e}")
        return ''

    def get_user_info(self, user_id: int) -> Dict[str, Any]:
        """获取用户信息"""
        try:
            url = f"{self.config.USER_INFO_API_URL}{user_id}&token={self._token}"
            logger.info(f"请求用户名称：{url}")
            response = requests.get(url)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            logger.error(f"Error fetching user info: {e}")
            return {} 